package xyz.xiaoshengfu.xrpc.provider;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import xyz.xiaoshengfu.xrpc.common.JsonUtils;
import xyz.xiaoshengfu.xrpc.config.XRpcConfig;
import xyz.xiaoshengfu.xrpc.data.XRpcRequestData;
import xyz.xiaoshengfu.xrpc.data.XRpcResources;
import xyz.xiaoshengfu.xrpc.data.XRpcResponseData;
import xyz.xiaoshengfu.xrpc.proxy.XRpcProxyClient;

/**
 * <p>Title: HeartBeatServerHandler</p>
 * <p>Description: xRPC服务提供者Handler</p>
 * @author xiaoshengfu(2439323118@qq.com)
 * @date 2020年2月25日
 */
public final class XRpcProviderServerHandler extends SimpleChannelInboundHandler<XRpcRequestData> {

	private final Map<String, Boolean> authMap = new ConcurrentHashMap<String, Boolean>();

	/**
	* 数据处理
	*/
	@Override
	public void channelRead0(ChannelHandlerContext context, XRpcRequestData data) throws Exception {
		XRpcResponseData xRpcResponseData = new XRpcResponseData();
		xRpcResponseData.setRequestID(data.getRequestID());
		if (data.getRequestType() == XRpcRequestData.RPC && authMap.get(context.channel().id().asShortText())
				&& StringUtils.isNotBlank(data.getInterfaceName())) {
			Object localService = XRpcResources.getLocalService(data.getInterfaceName());
			if (localService != null && StringUtils.isNoneBlank(data.getMethodName())) {
				try {
					Object object = XRpcProxyClient.invokeMethod(localService, data.getMethodName(),
							data.getMethodParameters());
					//System.out.println(object.getClass().getName());
					xRpcResponseData.setStatusCode(XRpcResponseData.OK);
					if (object != null) {
						xRpcResponseData.setReturnValue(object);
					}
				} catch (Exception e) {
					e.printStackTrace();
					xRpcResponseData.setStatusCode(XRpcResponseData.EXCEPTION);
					xRpcResponseData.setException(e);

				}
			} else {
				xRpcResponseData.setStatusCode(XRpcResponseData.ERROR);
			}
		} else if (data.getRequestType() == XRpcRequestData.AUTH) {
			if (XRpcResources.getLocalApplicationName().equals(data.getAcceptApplicationName())
					&& checkApplicationIsRegister(data.getAcceptApplicationName(), context.channel())) {
				authMap.put(context.channel().id().asShortText(), true);
				xRpcResponseData.setStatusCode(XRpcResponseData.AUTH_SUCCESS);
			} else {
				xRpcResponseData.setStatusCode(XRpcResponseData.AUTH_FAIL);
			}
		} else {
			xRpcResponseData.setStatusCode(XRpcResponseData.ERROR);
			context.writeAndFlush(xRpcResponseData).addListener(ChannelFutureListener.CLOSE);
			return;
		}
		context.writeAndFlush(xRpcResponseData);
	}

	/**
	 * 通道建立成功操作
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		String id = ctx.channel().id().asShortText();
		authMap.put(id, false);
	}

	/**
	 * 通道断开
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		String id = ctx.channel().id().asShortText();
		authMap.remove(id);
	}

	/**
	 * 异常处理
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		Channel channel = ctx.channel();
		if (channel.isActive()) {
			ctx.close();
		}
	}

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) {
			IdleStateEvent e = (IdleStateEvent) evt;
			switch (e.state()) {
			case READER_IDLE:
				handleReaderIdle(ctx);
				break;
			case WRITER_IDLE:
				handleWriterIdle(ctx);
				break;
			case ALL_IDLE:
				handleAllIdle(ctx);
				break;
			default:
				break;
			}
		}
	}

	/**
	 *<p>Title: handleReaderIdle</p>
	 *<p>Description: Channel在规定时间内没有读操作触发时间的回调函数</p>
	 * @param ctx
	 */
	private void handleReaderIdle(ChannelHandlerContext ctx) {
	}

	/**
	 *<p>Title: handleWriterIdle</p>
	 *<p>Description: Channel在规定时间内没有写操作触发时间的回调函数</p>
	 * @param ctx
	 */
	private void handleWriterIdle(ChannelHandlerContext ctx) {
	}

	/**
	 *<p>Title: handleAllIdle</p>
	 *<p>Description: Channel在规定时间内没有读和写操作触发时间的回调函数</p>
	 * @param ctx
	 */
	private void handleAllIdle(ChannelHandlerContext ctx) {
	}

	private boolean checkApplicationIsRegister(String applicationName, Channel channel) {
		String jsonXRpcConfig = XRpcResources.jedisClientPool.get("xRpcConfig:" + applicationName);
		if (StringUtils.isNoneBlank(jsonXRpcConfig)) {
			XRpcConfig xRpcConfig = JsonUtils.jsonToPojo(jsonXRpcConfig, XRpcConfig.class);
			if (applicationName.equals(xRpcConfig.getApplicationConfig().getName())) {
				InetSocketAddress ipSocket = (InetSocketAddress) channel.remoteAddress();
				String clientIp = ipSocket.getAddress().getHostAddress();
				if (clientIp != null && clientIp.equals(xRpcConfig.getApplicationConfig().getHostAddress())) {
					return true;
				}
			}
		}
		return false;
	}

}